﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "AbilitySystem/StarrySkyAttributeSet.h"

#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "GameplayEffectExtension.h"
#include "StarrySkyGameplayTags.h"
#include "GameFramework/Character.h"
#include "Net/UnrealNetwork.h"

UStarrySkyAttributeSet::UStarrySkyAttributeSet()
{
	const FStarrySkyGameplayTags& StarrySkyGameplayTags=FStarrySkyGameplayTags::Get();
	//FAttributeSignature StrengthDelegate;
	/**
	*BindStatic
	*绑定静态函数：将委托与静态函数进行绑定
	*无需实例：静态函数不需要类实例即可调用
	*性能优化：避免了实例化对象的开销
	**/
	//StrengthDelegate.BindStatic(GetStrengthAttribute);//通过代理绑定一个静态获取属性的函数
	//TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Primary_Strength,StrengthDelegate);//将游戏标签和代理添加一对一的关系

	//直接将函数指针作为map的值
	//重要属性
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Primary_Strength,GetStrengthAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Primary_Intelligence,GetIntelligenceAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Primary_Resilience,GetResilienceAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Primary_Vigor,GetVigorAttribute);

	//次要属性
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_Armor,GetArmorAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_ArmorPenetration,GetArmorPenetrationAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_BlockChance,GetBlockChanceAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_CriticalHitChance,GetCriticalHitChanceAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_CriticalHitDamage,GetCriticalHitDamageAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_CriticalHitResistance,GetCriticalHitResistanceAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_HealthRegeneration,GetHealthRegenerationAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_ManaRegeneration,GetManaRegenerationAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_MaxHealth,GetMaxHealthAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_Secondary_MaxMana,GetMaxManaAttribute);

	//主要属性
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_vital_Health,GetMaxHealthAttribute);
	TagsToAttributeSets.Add(StarrySkyGameplayTags.Attributes_vital_Mana,GetManaAttribute);
}

void UStarrySkyAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	//主要属性复制
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, Strength,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, Intelligence,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, Resilience,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, Vigor,COND_None, REPNOTIFY_Always);
	//次要属性复制
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, MaxHealth,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, MaxMana,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, HealthRegeneration,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, ManaRegeneration,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, Armor,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, ArmorPenetration,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, BlockChance,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, CriticalHitChance,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, CriticalHitDamage,COND_None, REPNOTIFY_Always);
	
	//主要属性复制
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, Health,COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UStarrySkyAttributeSet, Mana,COND_None, REPNOTIFY_Always);
}
/**
 * 预处理属性值函数，可以在属性真正更新之前的时候调用，
 * 比如在服务器上，在属性值改变之前，可以做一些处理，比如限制最大值等。
 * @param Attribute 
 * @param NewValue 
 */
void UStarrySkyAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
	Super::PreAttributeChange(Attribute, NewValue);
	if(Attribute == GetHealthAttribute())
	{
		NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth());
	}
	if(Attribute == GetManaAttribute())
	{
		NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxMana());
	}
}

void UStarrySkyAttributeSet::SetEffectProperties(const FGameplayEffectModCallbackData& Data,
	FEffectsProperties& Properties) const
{
	//源头==效果的发起者 目标==效果的目标能力系统的拥有者
	/******************************获取效果源的数据*******************************/
	//获取效果上下文句柄
	Properties.EffectContextHandle = Data.EffectSpec.GetContext();
	//通过上下问句柄获取效果源的能力系统，如果能力系统有效可以获取能力系统的拥有角色
	Properties.SourceASC =Properties.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent();
	if(IsValid(Properties.SourceASC)&&Properties.SourceASC->AbilityActorInfo.IsValid()&&Properties.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
	{
		Properties.SourceAvatarActor = Properties.SourceASC->AbilityActorInfo->AvatarActor.Get();
		Properties.SourceController=Properties.SourceASC->AbilityActorInfo->PlayerController.Get();
		//如果控制器没有获取到可以同故宫Actor转换未pwan获取
		if(Properties.SourceController==nullptr && Properties.SourceAvatarActor != nullptr)
		{
			if(const APawn* Pawn=Cast<APawn>(Properties.SourceAvatarActor))
			{
				Properties.SourceController=Pawn->GetController();
			}
		}
		//如果获取到了控制器就说明可以获取到游戏角色对象
		if(Properties.SourceController)
		{
			Properties.SourceCharacter=Cast<ACharacter>(Properties.SourceController->GetPawn());
		}
	}
	/******************************获取效果目标的数据*******************************/
	//通过目标获取目标Actor信息,这里如果目标不是具有能力系统就不会执行这个方法
	if(Data.Target.AbilityActorInfo.IsValid()&& Data.Target.AbilityActorInfo->AvatarActor.IsValid())
	{
		Properties.TargetAvatarActor=Data.Target.AbilityActorInfo->AvatarActor.Get();
		Properties.TargetController=Data.Target.AbilityActorInfo->PlayerController.Get();
		Properties.TargetCharacter=Cast<ACharacter>(Properties.TargetAvatarActor);
		Properties.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Properties.TargetAvatarActor);
	}
}

/**
 * 属性值真正改变时调用，比如在服务器上，在属性值改变之后，可以做一些处理，比如触发效果等。
 */
void UStarrySkyAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);
	FEffectsProperties Properties;//这个结构体传入方法中用来存储产生效果的各个属性值
	SetEffectProperties(Data, Properties);
	//限制属性值的范围
	if(Data.EvaluatedData.Attribute==GetHealthAttribute())
	{
		SetHealth(FMath::Clamp(GetHealth(), 0.0f, GetMaxHealth()));
	}
	if(Data.EvaluatedData.Attribute==GetManaAttribute())
	{
		SetMana(FMath::Clamp(GetMana(), 0.0f, GetMaxMana()));
	}
}

void UStarrySkyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, Health, OldHealth);
}

void UStarrySkyAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldMana) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, Mana, OldMana);
}

void UStarrySkyAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, MaxHealth, OldMaxHealth);
}

void UStarrySkyAttributeSet::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, MaxMana, OldMaxMana);
}

void UStarrySkyAttributeSet::OnRep_HealthRegeneration(const FGameplayAttributeData& OldHealthRegeneration) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, HealthRegeneration, OldHealthRegeneration);
}

void UStarrySkyAttributeSet::OnRep_ManaRegeneration(const FGameplayAttributeData& OldManaRegeneration) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, ManaRegeneration, OldManaRegeneration);
}

void UStarrySkyAttributeSet::OnRep_Strength(const FGameplayAttributeData& OldStrength) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, Strength, OldStrength);
}

void UStarrySkyAttributeSet::OnRep_Intelligence(const FGameplayAttributeData& OldIntelligence) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, Intelligence, OldIntelligence);
}

void UStarrySkyAttributeSet::OnRep_Resilience(const FGameplayAttributeData& OldResilience) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, Resilience, OldResilience);
}

void UStarrySkyAttributeSet::OnRep_Vigor(const FGameplayAttributeData& OldVigor) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, Vigor, OldVigor);
}

void UStarrySkyAttributeSet::OnRep_Armor(const FGameplayAttributeData& OldArmor) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, Armor, OldArmor);
}

void UStarrySkyAttributeSet::OnRep_ArmorPenetration(const FGameplayAttributeData& OldArmorPenetration) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, ArmorPenetration, OldArmorPenetration);
}

void UStarrySkyAttributeSet::OnRep_BlockChance(const FGameplayAttributeData& OldBlockChance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, BlockChance, OldBlockChance);
}

void UStarrySkyAttributeSet::OnRep_CriticalHitChance(const FGameplayAttributeData& OldCriticalHitChance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, CriticalHitChance, OldCriticalHitChance);
}

void UStarrySkyAttributeSet::OnRep_CriticalHitDamage(const FGameplayAttributeData& OldCriticalHitDamage) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, CriticalHitDamage, OldCriticalHitDamage);
}

void UStarrySkyAttributeSet::OnRep_CriticalHitResistance(const FGameplayAttributeData& OldCriticalHitResistance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UStarrySkyAttributeSet, CriticalHitResistance, OldCriticalHitResistance);
}